package com.treeroot.aopservice.config;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * @Description:
 * @Author:treeRoot
 * @Email:553503639@qq.com
 * @Date: Create in 20:54 / 2022/9/1
 */
@Aspect
@Component
@Slf4j
public class AOPAspect {
    /**
     * 表达式中的第一个* 代表方法的修饰范围	（public 、private、protected）
     * 如果取值是*，则表示所有范围
     * 执行所有的方法  execution（public *(..)）
     * 执行任意的set方法	execution（* set*(..)）
     * 执行指定包下的任意类的任意方法	execution（* com.xxx.service.*.*(..)）
     * 执行指定包及子包下的任意类的任意方法	execution（* com.xxx.service..*.*(..)）
     */
    @Pointcut("execution(* com.treeroot.aopservice.controller.AOPTest.*(..))")
    public void AOPLog() {
    }
    /**
     *  前置通知：在连接点之前执行的通知
     *  注意：这里用到了JoinPoint和RequestContextHolder。
     * 1）、通过JoinPoint可以获得通知的签名信息，如目标方法名、目标方法参数信息等；
     * 2）、通过RequestContextHolder来获取请求信息，Session信息；
     */
    @Before("AOPLog()")
    public void before(JoinPoint jp) {
        String name = jp.getSignature().getName();
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        log.info("URL : " + request.getRequestURL().toString());
        log.info("HTTP_METHOD : " + request.getMethod());
        log.info("IP : " + request.getRemoteAddr());
        log.info("CLASS_METHOD : " + jp.getSignature().getDeclaringTypeName() + "." + jp.getSignature().getName());
        log.info("ARGS : " + Arrays.toString(jp.getArgs()));

        System.out.println("AOP通知：" + name + "方法开始执行");
    }

    @After("AOPLog()")
    public void after() {
        System.out.println("之后执行！");
    }

    @AfterThrowing("AOPLog()")
    public void beforeException() {
        System.out.println("方法报错时运行");
    }

    @AfterReturning(returning = "ret",pointcut = "AOPLog()")
    public void afterEnd(Object ret) {
        // 处理完请求，返回内容(获取的是前面方法返回的结果)
        log.info("RESPONSE : " + ret);
        System.out.println("方法返回时候调用");
    }
}
